package com.niedziela12.pai.sessionbeans;

import com.niedziela12.pai.ejb.dto.RoleDTO;
import com.niedziela12.pai.ejb.dto.UserDTO;
import com.niedziela12.pai.ejb.dto.UserRolesDTO;
import com.niedziela12.pai.ejb.entities.Role;
import com.niedziela12.pai.ejb.user.endpoints.UserEndpointLocal;
import com.niedziela12.pai.ejb.user.facades.RoleFacadeLocal;
import com.niedziela12.pai.sessionbeans.util.JsfUtil;
import com.niedziela12.pai.sessionbeans.util.PaginationHelper;

import java.io.Serializable;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.SessionScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.SelectItem;
import javax.servlet.http.HttpSession;

@ManagedBean(name = "userController")
@SessionScoped
public class UserController implements Serializable {

    private UserDTO current;
    private DataModel items = null;
    @EJB
    private UserEndpointLocal userEndpoint;
    @EJB
    private RoleFacadeLocal roleFacade;
    private PaginationHelper pagination;
    private int selectedItemIndex;
    private List<String> selectedRoleList;
    private List<UserRolesDTO> roleSelectItems;
    
    @ManagedProperty(value = "#{userRights}")
    private UserRights userRights;

    public void setUserRights(UserRights userRights) {
        this.userRights = userRights;
    }

    public UserController() {
    }
    
    public String logOut() {
        HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
        if (session != null) {
            session.invalidate();
            return "index";
        }
        return null;
    }

    public UserDTO getSelected() {
        if (current == null) {
            current = new UserDTO();
            selectedItemIndex = -1;
        }
        return current;
    }
    
    public void setSelectedRoleList(List<String> selectedRoleList) {
        this.selectedRoleList = selectedRoleList;
    }
    
     public List<String> getSelectedRoleList() {
        return selectedRoleList;
    }
     
     public List<UserRolesDTO> getRoleSelectItems() {
        return roleSelectItems;
    }

    public PaginationHelper getPagination() {
        if (pagination == null) {
            pagination = new PaginationHelper(10) {
                @Override
                public int getItemsCount() {
                    return userEndpoint.getUsersCount();
                }

                @Override
                public DataModel createPageDataModel() {
                    return new ListDataModel(userEndpoint.getUsers(getPageFirstItem(), getPageFirstItem() + getPageSize()));
                }
            };
        }
        return pagination;
    }
    
    public String prepareList() {
        recreateModel();
        return userRights.isAdmin()? "adminListUsers" : "moderatorListUsers";
    }

    public String prepareView() {
        current = (UserDTO) getItems().getRowData();
        selectedItemIndex = pagination.getPageFirstItem() + getItems().getRowIndex();
        return userRights.isAdmin()? "adminViewUser" : "moderatorViewUser";
    }
    
    public String prepareCreate() {
        current = new UserDTO();
        selectedItemIndex = -1;
        
        List<RoleDTO> roles = userEndpoint.findRolesDef();
        roleSelectItems = new ArrayList<>();
        selectedRoleList = new ArrayList<>();
        int id = 1;
        for (RoleDTO ur : roles) {
            UserRolesDTO tmp = new UserRolesDTO();
            tmp.setIdRole(ur);
            tmp.setId(id++);
            roleSelectItems.add(tmp);
        }
        return "adminAddUsers";
    }

    public String create() {
        try {
            current.setPassword(createHash(current.getPassword()));
            userEndpoint.addNewUser(current);
            current = userEndpoint.getUserByLogin(current.getLogin());
            
            OUTER:
            for (UserRolesDTO ur : roleSelectItems) {
                ur.setIdLogin(current);
                for (String selected : selectedRoleList) {
                    if(Integer.parseInt(selected) == ur.getId()){
                        ur.setActive(true);
                        ur.setId(null);
                        continue OUTER;
                    }
                 }
                ur.setActive(false);
                ur.setId(null);
            }
            userEndpoint.updateUserRoles(roleSelectItems);
            
            JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("userCreated"));
            return prepareList();
        } catch (Exception e) {
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
            return null;
        }
    }
    
    public String register() {
        try {
            current.setPassword(createHash(current.getPassword()));
            userEndpoint.addNewUser(current);
            current = userEndpoint.getUserByLogin(current.getLogin());
            List<RoleDTO> roles = userEndpoint.findRolesDef();
            List<UserRolesDTO>userRoles = new ArrayList<>();
            for (RoleDTO ur : roles) {
                UserRolesDTO tmp = new UserRolesDTO();
                tmp.setActive("client".equals(ur.getName()));
                tmp.setIdLogin(current);
                tmp.setIdRole(ur);
                userRoles.add(tmp);
            }
            userEndpoint.updateUserRoles(userRoles);
            JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("userCreated"));
            return "index";
        } catch (Exception e) {
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
            return null;
        }
    }

    public String prepareEdit() {
        if(userRights.isModerator() || userRights.isAdmin()){
            current = (UserDTO) getItems().getRowData();
            selectedItemIndex = pagination.getPageFirstItem() + getItems().getRowIndex();
        }
        else{
            current = userEndpoint.getUserByLogin(FacesContext.getCurrentInstance().getExternalContext().getRemoteUser());
        }
        prepareRolesList();
        return userRights.isAdmin() ? "adminEditUser" : (userRights.isModerator() ? "moderatorEditUser" : "clientEditUser");
    }

    public String update() {
        try {
            current.setPassword(createHash(current.getPassword()));
            userEndpoint.updateUserInfo(current);
            OUTER:
            for (UserRolesDTO ur : roleSelectItems) {
                for (String selected : selectedRoleList) {
                    if(Integer.parseInt(selected) == ur.getId()){
                        ur.setActive(true);
                        continue OUTER;
                    }
                 }
                ur.setActive(false);// nie zaznaczona w liscie zostala dlatego wylaczam
            }
            userEndpoint.updateUserRoles(roleSelectItems);
            JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("userUpdated"));
            return userRights.isAdmin() ? "adminViewUser" : (userRights.isModerator() ? "moderatorViewUser" : "index");
        } catch (Exception e) {
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
            return null;
        }
    }

    public String destroy() {
        current = (UserDTO) getItems().getRowData();
        selectedItemIndex = pagination.getPageFirstItem() + getItems().getRowIndex();
        performDestroy();
        recreatePagination();
        recreateModel();
        return "adminListUsers";
    }

    public String destroyAndView() {
        performDestroy();
        recreateModel();
        updateCurrentItem();
        if (selectedItemIndex >= 0) {
            return "adminViewUser";
        } else {
            // all items were removed - go back to list
            recreateModel();
            return "adminListUsers";
        }
    }

    private void performDestroy() {
        try {
            userEndpoint.deleteUser(current);
            JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("userDeleted"));
        } catch (Exception e) {
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
        }
    }

    private void updateCurrentItem() {
        int count = userEndpoint.getUsersCount();
        if (selectedItemIndex >= count) {
            // selected index cannot be bigger than number of items:
            selectedItemIndex = count - 1;
            // go to previous page if last page disappeared:
            if (pagination.getPageFirstItem() >= count) {
                pagination.previousPage();
            }
        }
        if (selectedItemIndex >= 0) {
            current = userEndpoint.getUsers(selectedItemIndex, selectedItemIndex + 1).get(0);
        }
    }

    public DataModel getItems() {
        if (items == null) {
            items = getPagination().createPageDataModel();
        }
        return items;
    }

    private void recreateModel() {
        items = null;
    }

    private void recreatePagination() {
        pagination = null;
    }

    public String next() {
        getPagination().nextPage();
        recreateModel();
        return userRights.isAdmin()? "adminListUsers" : "moderatorListUsers";
    }

    public String previous() {
        getPagination().previousPage();
        recreateModel();
        return userRights.isAdmin()? "adminListUsers" : "moderatorListUsers";
    }

    public SelectItem[] getItemsAvailableSelectMany() {
        return JsfUtil.getSelectItems(userEndpoint.getAllUsers(), false);
    }

    public SelectItem[] getItemsAvailableSelectOne() {
        return JsfUtil.getSelectItems(userEndpoint.getAllUsers(), true);
    }

    private void prepareRolesList() {
        //zalozenie ze uzytkownik ma wszystkie role lecz nieaktywne sa niektore
        List<UserRolesDTO> userRoles = userEndpoint.getUsersRoles(current);
        selectedRoleList = new ArrayList<>();
        roleSelectItems = new ArrayList<>();
        for (UserRolesDTO role : userRoles) {
            roleSelectItems.add(role);
            if(role.isActive())
                selectedRoleList.add(""+role.getId());
        }
    }

    @FacesConverter(forClass = UserDTO.class)
    public static class userControllerConverter implements Converter {

        public Object getAsObject(FacesContext facesContext, UIComponent component, String value) {
            if (value == null || value.length() == 0) {
                return null;
            }
            UserController controller = (UserController) facesContext.getApplication().getELResolver().
                    getValue(facesContext.getELContext(), null, "userController");
            return controller.userEndpoint.getUser(getKey(value));
        }

        java.lang.Long getKey(String value) {
            java.lang.Long key;
            key = Long.valueOf(value);
            return key;
        }

        String getStringKey(java.lang.Long value) {
            StringBuffer sb = new StringBuffer();
            sb.append(value);
            return sb.toString();
        }

        public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
            if (object == null) {
                return null;
            }
            if (object instanceof UserDTO) {
                UserDTO o = (UserDTO) object;
                return getStringKey(o.getId());
            } else {
                throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: " + UserDTO.class.getName());
            }
        }
    }
    
    public String createHash(String text){
        try{
            MessageDigest algorithm = MessageDigest.getInstance("MD5");
            algorithm.reset();
            algorithm.update(text.getBytes());
            byte messageDigest[] = algorithm.digest();

            StringBuffer hexString = new StringBuffer();
            for (int i=0;i<messageDigest.length;i++) {
                    hexString.append(Integer.toString((messageDigest[i] & 0xff) + 0x100, 16).substring(1));
            }
            return hexString.toString();
        }catch(NoSuchAlgorithmException nsae){
            return "##error##";
        }
    }
}
